package com.task.controller.util;

import ohos.app.Context;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class ReflectionsUtil {

    private static Set<String> entryNames;

    private static ClassLoader classLoader;

    /**
     * 反射工具初始化
     *
     * @param context      上下文对象
     */
    public static void init(Context context) {
        entryNames = new HashSet<>();
        classLoader = Thread.currentThread().getContextClassLoader();
        String resourcePath = context.getBundleResourcePath();
        List<String> names = new ArrayList<>(Collections.singletonList(resourcePath));
        names.add(resourcePath);
        try {
            for (String name : names) {
                Class<?> aClass = Class.forName("dalvik.system.DexFile");
                Object o = aClass.getDeclaredConstructor(String.class).newInstance(name);
                Method[] methods = o.getClass().getDeclaredMethods();
                for (Method method : methods) {
                    if (method.getName().equals("entries")) {
                        method.setAccessible(true);
                        Enumeration<String> enumeration = (Enumeration<String>) method.invoke(o);
                        while (enumeration.hasMoreElements()) {
                            String nextElement = enumeration.nextElement();
                            if (nextElement.contains("dalvik.system") || nextElement.contains("lombok")) {
                                continue;
                            }
                            try {
                                classLoader.loadClass(nextElement);
                            } catch (ClassNotFoundException e) {
                                continue;
                            }
                            entryNames.add(nextElement);
                        }
                        break;
                    }
                }
            }
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取拥有指定注解的类的Class对象
     *
     * @param cls 指定的注解的Class对象
     *            注意此注解不能被子类继承
     * @return 拥有cls注解的所有类的类对象的Set集合
     */
    public static Set<Class<?>> getClassByAnnotationClass(Class<? extends Annotation> cls) {
        Set<Class<?>> classes = new HashSet<>();
        Class<?> entryClass = null;
        try {
            for (String entryName : entryNames) {
                entryClass = Class.forName(entryName, true, classLoader);
                Annotation annotation = entryClass.getAnnotation(cls);
                if (annotation != null) {
                    classes.add(entryClass);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return classes.isEmpty() ? null : classes;
    }

    /**
     * 返回拥有指定注解的成员变量的Field变量的Set集合
     *
     * @param cls 指定注解的Class对象
     * @return 返回拥有cls注解的所有成员变量的Set集合
     */
    public static Set<Field> getFieldByAnnotation(Class<? extends Annotation> cls) {
        Set<Field> fields = new HashSet<>();
        Class<?> aClass = null;
        try {
            for (String entryName : entryNames) {
                aClass = Class.forName(entryName);
                Field[] declaredFields = aClass.getDeclaredFields();
                for (Field field : declaredFields) {
                    Annotation declaredAnnotation = field.getDeclaredAnnotation(cls);
                    if (declaredAnnotation == null) {
                        continue;
                    }
                    fields.add(field);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return fields.isEmpty() ? null : fields;
    }

    /**
     * 获取拥有指定注解的Method对象的Set集合
     *
     * @param cls 指定注解的Class对象
     * @return 拥有cls注解的Method的Set集合
     */
    public static Set<Method> getMethodByAnnotation(Class<? extends Annotation> cls) {
        Set<Method> methods = new HashSet<>();
        Class<?> aClass = null;
        try {
            for (String entryName : entryNames) {
                aClass = Class.forName(entryName, true, classLoader);
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    Annotation declaredAnnotation = method.getDeclaredAnnotation(cls);
                    if (declaredAnnotation == null) {
                        continue;
                    }
                    methods.add(method);
                }

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return methods.isEmpty() ? null : methods;
    }
}
